//! # 键盘映射器
//!
//! 把 Midi 键盘按键映射到本地键盘按键

mod key_state;

use std::{collections::HashMap, time::Duration};

use async_trait::async_trait;
use midly::{
    num::{u4, u7},
    MidiMessage,
};
use serde_json::Value;

use crate::driver::keyboard::{key_code::KeyCode, KeyboardDriver};

use self::key_state::KeyState;

use super::{CreateMapperError, MidiMapper};

/// 键盘映射器
pub struct KeyMapper {
    /// 键盘映射器
    driver: KeyboardDriver,
    /// 音符到键位的映射
    note2keys: HashMap<u7, KeyState>,
}

impl KeyMapper {
    /// 给定音符到键位的映射，创建键盘映射器
    pub fn new(note2keys: HashMap<u7, Vec<KeyCode>>) -> Self {
        KeyMapper {
            driver: KeyboardDriver::default(),
            note2keys: note2keys
                .iter()
                .map(|(k, v)| (k.clone(), KeyState::new(v.clone())))
                .collect(),
        }
    }

    /// 根据 JSON 规则创建键盘映射器
    pub fn from_rule(mut rule: Value) -> Result<Self, CreateMapperError> {
        // 读取 rule object
        let obj = rule.as_object_mut().ok_or(CreateMapperError::IllegalRule(
            "KeyMapper 的 rule 必须是 JSON 对象".to_owned(),
        ))?;

        // 初始化 note2keys
        let mut note2keys: HashMap<u7, KeyState> = HashMap::new();

        // 遍历 rule
        for (k, v) in obj {
            // 解析 key 为 Midi Note
            let k: u8 = k
                .parse()
                .map_err(|e| CreateMapperError::IllegalRule(format!("{e:?}")))?;

            // 解析 value 为 key code array
            let v: Vec<KeyCode> = if v.is_array() {
                serde_json::from_value(v.take())
                    .map_err(|e| CreateMapperError::IllegalRule(format!("{e:?}")))?
            } else {
                let v = serde_json::from_value(v.take())
                    .map_err(|e| CreateMapperError::IllegalRule(format!("{e:?}")))?;
                vec![v]
            };

            // 添加到 note2keys 中
            note2keys.insert(u7::from_int_lossy(k), KeyState::new(v));
        }

        // 返回
        Ok(KeyMapper {
            note2keys,
            driver: KeyboardDriver::default(),
        })
    }

    /// 清除所有按下的键
    fn clear_pressed(&mut self) {
        for (_, state) in &mut self.note2keys {
            if state.pressed {
                state.keys_up(&self.driver);
            }
        }
    }
}

#[async_trait]
impl MidiMapper for KeyMapper {
    /// 开始映射时，清理按键
    async fn start(&mut self) {
        self.clear_pressed();
    }

    /// 处理 MIDI 事件
    async fn on_midi(&mut self, _: usize, _: u4, message: MidiMessage) {
        match message {
            midly::MidiMessage::NoteOn { key, vel: _ } => {
                // 按下按键
                if let Some(state) = self.note2keys.get_mut(&key) {
                    state.keys_down(&self.driver);
                }
            }
            midly::MidiMessage::NoteOff { key, vel: _ } => {
                // 释放按键
                if let Some(state) = self.note2keys.get_mut(&key) {
                    state.keys_up(&self.driver);
                }
            }
            _ => (),
        }
    }

    /// 结束映射时，清理按键
    async fn finish(&mut self, _: Duration) {
        self.clear_pressed();
    }
}
